package com.mini.grpc.retry;

import com.mini.grpc.common.Status;

import java.util.Random;
import java.util.Set;
import java.util.EnumSet;

/**
 * 指数退避重试策略实现
 * 使用指数退避算法计算重试延迟时间
 * 
 * @author Mini-gRPC
 */
public class ExponentialBackoffRetryPolicy implements RetryPolicy {
    
    private static final String NAME = "exponential_backoff";
    
    // 默认可重试的状态码
    private static final Set<Status.Code> DEFAULT_RETRYABLE_CODES = EnumSet.of(
            Status.Code.UNAVAILABLE,
            Status.Code.DEADLINE_EXCEEDED,
            Status.Code.RESOURCE_EXHAUSTED,
            Status.Code.ABORTED,
            Status.Code.INTERNAL
    );
    
    private final int maxAttempts;
    private final long initialDelayMillis;
    private final long maxDelayMillis;
    private final double multiplier;
    private final double jitter;
    private final Set<Status.Code> retryableCodes;
    private final Random random;
    
    /**
     * 构造函数
     * 
     * @param maxAttempts 最大重试次数
     * @param initialDelayMillis 初始延迟时间（毫秒）
     * @param maxDelayMillis 最大延迟时间（毫秒）
     * @param multiplier 延迟倍数
     * @param jitter 抖动因子（0-1之间）
     * @param retryableCodes 可重试的状态码集合
     */
    public ExponentialBackoffRetryPolicy(int maxAttempts, 
                                       long initialDelayMillis,
                                       long maxDelayMillis,
                                       double multiplier,
                                       double jitter,
                                       Set<Status.Code> retryableCodes) {
        if (maxAttempts <= 0) {
            throw new IllegalArgumentException("maxAttempts must be positive");
        }
        if (initialDelayMillis <= 0) {
            throw new IllegalArgumentException("initialDelayMillis must be positive");
        }
        if (maxDelayMillis <= 0) {
            throw new IllegalArgumentException("maxDelayMillis must be positive");
        }
        if (multiplier <= 1.0) {
            throw new IllegalArgumentException("multiplier must be greater than 1.0");
        }
        if (jitter < 0.0 || jitter > 1.0) {
            throw new IllegalArgumentException("jitter must be between 0.0 and 1.0");
        }
        
        this.maxAttempts = maxAttempts;
        this.initialDelayMillis = initialDelayMillis;
        this.maxDelayMillis = maxDelayMillis;
        this.multiplier = multiplier;
        this.jitter = jitter;
        this.retryableCodes = retryableCodes != null ? retryableCodes : DEFAULT_RETRYABLE_CODES;
        this.random = new Random();
    }
    
    /**
     * 使用默认参数的构造函数
     */
    public ExponentialBackoffRetryPolicy() {
        this(3, 100, 30000, 2.0, 0.1, DEFAULT_RETRYABLE_CODES);
    }
    
    /**
     * 构建器
     */
    public static Builder newBuilder() {
        return new Builder();
    }
    
    @Override
    public boolean shouldRetry(Status status, int attemptCount) {
        if (attemptCount >= maxAttempts) {
            return false;
        }
        
        return retryableCodes.contains(status.getCode());
    }
    
    @Override
    public long getRetryDelayMillis(int attemptCount) {
        if (attemptCount <= 1) {
            return initialDelayMillis;
        }
        
        // 计算指数退避延迟
        double delay = initialDelayMillis * Math.pow(multiplier, attemptCount - 1);
        
        // 限制最大延迟
        delay = Math.min(delay, maxDelayMillis);
        
        // 添加抖动
        if (jitter > 0) {
            double jitterRange = delay * jitter;
            double jitterValue = (random.nextDouble() * 2 - 1) * jitterRange;
            delay += jitterValue;
        }
        
        return Math.max(0, (long) delay);
    }
    
    @Override
    public int getMaxAttempts() {
        return maxAttempts;
    }
    
    @Override
    public String getName() {
        return NAME;
    }
    
    @Override
    public String toString() {
        return "ExponentialBackoffRetryPolicy{" +
                "maxAttempts=" + maxAttempts +
                ", initialDelayMillis=" + initialDelayMillis +
                ", maxDelayMillis=" + maxDelayMillis +
                ", multiplier=" + multiplier +
                ", jitter=" + jitter +
                ", retryableCodes=" + retryableCodes +
                '}';
    }
    
    /**
     * 构建器类
     */
    public static class Builder {
        private int maxAttempts = 3;
        private long initialDelayMillis = 100;
        private long maxDelayMillis = 30000;
        private double multiplier = 2.0;
        private double jitter = 0.1;
        private Set<Status.Code> retryableCodes = DEFAULT_RETRYABLE_CODES;
        
        /**
         * 设置最大重试次数
         * 
         * @param maxAttempts 最大重试次数
         * @return 构建器实例
         */
        public Builder setMaxAttempts(int maxAttempts) {
            this.maxAttempts = maxAttempts;
            return this;
        }
        
        /**
         * 设置初始延迟时间
         * 
         * @param initialDelayMillis 初始延迟时间（毫秒）
         * @return 构建器实例
         */
        public Builder setInitialDelayMillis(long initialDelayMillis) {
            this.initialDelayMillis = initialDelayMillis;
            return this;
        }
        
        /**
         * 设置最大延迟时间
         * 
         * @param maxDelayMillis 最大延迟时间（毫秒）
         * @return 构建器实例
         */
        public Builder setMaxDelayMillis(long maxDelayMillis) {
            this.maxDelayMillis = maxDelayMillis;
            return this;
        }
        
        /**
         * 设置延迟倍数
         * 
         * @param multiplier 延迟倍数
         * @return 构建器实例
         */
        public Builder setMultiplier(double multiplier) {
            this.multiplier = multiplier;
            return this;
        }
        
        /**
         * 设置抖动因子
         * 
         * @param jitter 抖动因子（0-1之间）
         * @return 构建器实例
         */
        public Builder setJitter(double jitter) {
            this.jitter = jitter;
            return this;
        }
        
        /**
         * 设置可重试的状态码
         * 
         * @param retryableCodes 可重试的状态码集合
         * @return 构建器实例
         */
        public Builder setRetryableCodes(Set<Status.Code> retryableCodes) {
            this.retryableCodes = retryableCodes;
            return this;
        }
        
        /**
         * 构建重试策略实例
         * 
         * @return 重试策略实例
         */
        public ExponentialBackoffRetryPolicy build() {
            return new ExponentialBackoffRetryPolicy(
                    maxAttempts, initialDelayMillis, maxDelayMillis,
                    multiplier, jitter, retryableCodes);
        }
    }
} 